#include "parser.h"

Parser::Parser(const std::vector<Rules>& first_r, const std::vector<Rules>& follow_r)
    : output_tree(0)
{
    first_rules.insert(first_rules.begin(), first_r.begin(), first_r.end());
    follow_rules.insert(follow_rules.begin(), follow_r.begin(), follow_r.end());
    __fillFirstSet();
    __fillFollowSet();
}

void Parser::__fillFirstSet()
{
    std::map<std::string, std::map<std::string, std::string> > m;
    for (uint i = 0; i < first_rules.size(); i++) { // 1 крок
        std::string nt_symbol = first_rules[i].getNTSymbol();
        for (uint j = 0; j < first_rules[i].size(); j++) {
            std::string specific_rule = first_rules[i].getRule(j);
            std::string rule_element;
            rule_element = specific_rule[0]; // перший символ правила
            if(isalpha(specific_rule[0]))
            {
                uint ascii_code;
                if (islower(specific_rule[0])) ascii_code = 97;
                else ascii_code = 65;
                for (uint z = 1; z < specific_rule.size(); z++) {
                    if (((specific_rule[z] >= ascii_code) && (specific_rule[z] <= ascii_code + 25)) || isdigit(specific_rule[z]))
                        rule_element += specific_rule[z];
                    else break;
                }
                if (m[nt_symbol][rule_element] == rule_element) continue;
                first[nt_symbol].push_back(rule_element);
                m[nt_symbol][rule_element] = rule_element;
            }
            else
            {
                for (uint z = 1; z < specific_rule.size(); z++) {
                    if (isalpha(specific_rule[z]) || isdigit(specific_rule[z]) || isspace(specific_rule[z])) break;
                    rule_element += specific_rule[z];
                }
                if (m[nt_symbol][rule_element] == "")
                {
                    first[nt_symbol].push_back(rule_element);
                    m[nt_symbol][rule_element] = rule_element;
                }
            }
        }
    }
    while (true) { // крок2
        std::map<std::string, std::vector<std::string> > m2 = first;
        for (uint i = 0; i < first_rules.size(); i++) {
            std::string nt_symbol = first_rules[i].getNTSymbol();
            for (uint j = 0; j < first[nt_symbol].size(); j++) {
                if (!isupper(first[nt_symbol][j][0])) continue;
                for (uint z = 0; z < first[first[nt_symbol][j]].size(); z++) {
                    if (m[nt_symbol][first[first[nt_symbol][j]][z]] != "") continue;
                    first[nt_symbol].push_back(first[first[nt_symbol][j]][z]);
                    m[nt_symbol][first[first[nt_symbol][j]][z]] = first[first[nt_symbol][j]][z];
                }
            }
        }
        if (m2 == first) //крок 3-4
        {
            for(uint i = 0; i < first_rules.size(); i++)
            {
                std::string nt_symbol = first_rules[i].getNTSymbol();
                for (uint j = 0; j < first[nt_symbol].size(); j++)
                    if (isupper(first[nt_symbol][j][0]))
                        first[nt_symbol].erase(first[nt_symbol].begin() + j--);
            }
            break;
        }
    }
    for (uint i = 0; i < first_rules.size(); i++) {
        std::cout << first_rules[i].getNTSymbol() << ":\t";
        for (uint j = 0; j < first[first_rules[i].getNTSymbol()].size(); j++)
            std::cout << first[first_rules[i].getNTSymbol()][j] << '\t';
        std::cout << '\n';
    }
}

void Parser::__fillFollowSet()
{
    std::map<std::string, std::map<std::string, std::string> > m;
    for (uint i = 0; i < follow_rules.size(); i++) { // крок 1
        for (uint j = 0; j < follow_rules[i].size(); j++) {
            std::string specific_rule = follow_rules[i].getRule(j);
            std::string str;
            for (uint z = 0, c = 0; z < follow_rules[i].getRule(j).size(); z++) {
                if (z > 0) c = 1;
                if (isupper(specific_rule[z]) || (isdigit(specific_rule[z]) && isupper(specific_rule[z - c]))) // виділяємо NT-символи
                {
                    str += specific_rule[z];
                    continue;
                }
                if (!str.empty())
                {
                    while (isspace(specific_rule[z])) ++z; // пробілами в правилах розділяються розміщені підряд NT-символи
                    std::string rule_element;
                    if(isalpha(specific_rule[z]))
                    {
                        uint ascii_code;
                        if (islower(specific_rule[z])) ascii_code = 97; // VT-символ (літери)
                        else ascii_code = 65; // VN-символ
                        for (uint k = z; k < specific_rule.size(); k++) {
                            if (((specific_rule[k] >= ascii_code) && (specific_rule[k] <= ascii_code + 25)) || isdigit(specific_rule[k]))
                                rule_element += specific_rule[k];
                            else break;
                        }
                        if (m[str][rule_element] == "")
                        {
                            m[str][rule_element] = rule_element;
                            follow[str].push_back(rule_element);
                        }
                        --z;
                    }
                    else // VT-символ (не літера) - (, {, >, ==, >= та ін.
                    {
                        for (uint k = z; k < specific_rule.size(); k++) {
                            if (isalpha(specific_rule[k]) || isdigit(specific_rule[k]) || isspace(specific_rule[k])) break;
                            rule_element += specific_rule[k];
                        }
                        if (m[str][rule_element] == "")
                        {
                            m[str][rule_element] = rule_element;
                            follow[str].push_back(rule_element);
                        }
                    }
                }
                str.clear();
            }
        }
    }
    follow["S"].push_back("^"); // крок 2
    while (true) {
        std::map<std::string, std::vector<std::string> > m2 = follow;
        for (uint i = 0; i < follow_rules.size(); i++) { // крок 3
            std::string nt_symbol = follow_rules[i].getNTSymbol();
            for (uint j = 0; j < follow[nt_symbol].size(); j++) {
                std::string j_nt_symbol = follow[nt_symbol][j];
                if (!isupper(j_nt_symbol[0])) continue;
                for (uint z = 0; z < first[j_nt_symbol].size(); z++) {
                    if (m[nt_symbol][first[j_nt_symbol][z]] == "")
                    {
                        m[nt_symbol][first[j_nt_symbol][z]] = first[j_nt_symbol][z];
                        follow[nt_symbol].push_back(first[j_nt_symbol][z]);
                    }
                }
            }
        }
        for (uint i = 0; i < follow_rules.size(); i++) { // крок 4
            std::string nt_symbol = follow_rules[i].getNTSymbol();
            for (uint j = 0; j < follow[nt_symbol].size(); j++) {
                std::string j_nt_symbol = follow[nt_symbol][j];
                if (!isupper(j_nt_symbol[0])) continue;
                Rules rule = findRule(j_nt_symbol, follow_rules);
                if (rule.getRule(0) != "^") continue;
                for (uint z = 0; z < follow[j_nt_symbol].size(); z++)
                    if (m[nt_symbol][follow[j_nt_symbol][z]] == "")
                    {
                        follow[nt_symbol].push_back(follow[j_nt_symbol][z]);
                        m[nt_symbol][follow[j_nt_symbol][z]] = follow[j_nt_symbol][z];
                    }
            }
        }
        for (uint i = 0; i < follow_rules.size(); i++) { // крок 5
            for (uint j = 0; j < follow_rules.size(); j++) {
                std::string j_nt_symbol = follow_rules[j].getNTSymbol();
                std::string nt_symbol = follow_rules[i].getNTSymbol();
                if (i == j) continue;
                std::vector<std::string> tmp;
                std::map<std::string, std::map<std::string, std::string> > tmp_m = m;
                for (uint z = 0; z < follow[j_nt_symbol].size(); z++) {
                    if (tmp_m[nt_symbol][follow[j_nt_symbol][z]] == "")
                    {
                        tmp.push_back(follow[j_nt_symbol][z]);
                        tmp_m[nt_symbol][follow[j_nt_symbol][z]] = follow[j_nt_symbol][z];
                    }
                }
                for (uint z = 0; z < follow_rules[j].size(); z++) {
                    int r_size = follow_rules[j].getRule(z).size();
                    int nt_size = nt_symbol.size();
                    if (r_size >= nt_size)
                    {
                        if ((follow_rules[j].getRule(z).substr(r_size - nt_size, r_size) == nt_symbol) &&
                                (follow[nt_symbol].size() == 0))
                            follow[nt_symbol].push_back("###");
                        else if (follow[nt_symbol].size() > 0)
                            if ((follow_rules[j].getRule(z).substr(r_size - nt_size, r_size) == nt_symbol) &&
                                    (follow[nt_symbol].back() != "###"))
                                follow[nt_symbol].push_back("###");
                    }
                }
                if (follow[nt_symbol].size() > 0)
                    if (follow[nt_symbol].back() == "###")
                    {
                        m = tmp_m;
                        std::vector<std::string>::iterator b  = follow[nt_symbol].begin();
                        int s = follow[nt_symbol].size() - 1;
                        follow[nt_symbol].erase(b + s);
                        follow[nt_symbol].insert(b + s, tmp.begin(), tmp.end());
                    }
            }
        }
        if (m2 == follow) //крок 6-7
        {
            for(uint i = 0; i < follow_rules.size(); i++)
            {
                std::string nt_symbol = follow_rules[i].getNTSymbol();
                for (int j = 0; j < follow[nt_symbol].size(); j++)
                    if (isupper(follow[nt_symbol][j][0]))
                        follow[nt_symbol].erase(follow[nt_symbol].begin() + j--);
            }
            break;
        }
    }
    for (uint i = 0; i < follow_rules.size(); i++) {
        std::cout << follow_rules[i].getNTSymbol() << ":\t";
        for (uint j = 0; j < follow[follow_rules[i].getNTSymbol()].size(); j++)
            std::cout << follow[follow_rules[i].getNTSymbol()][j] << "\t";
        std::cout << '\n';
    }
}

int Parser::__sysParsingCompletion(std::string stack)
{
    std::string nt_symbol;
    for (uint i = 0; i < stack.size(); i++) {
        if (isupper(stack[i]) || isdigit(stack[i]))
        {
            nt_symbol += stack[i];
            continue;
        }
        if (!nt_symbol.empty())
        {
            Rules rule = findRule(nt_symbol, follow_rules);
            if (rule.getRule(0) == "^") // пусте правило завжди є першим правилом для NT-символа, який має це правило
            {
                std::vector<std::string>::iterator it = std::find(follow[nt_symbol].begin(), follow[nt_symbol].end(), "^");
                if (it != follow[nt_symbol].end()) { nt_symbol.clear(); continue; }
            }
            return 1; // синтаксична помилка
        }
        if (isspace(stack[i])) continue;
        if (!(isupper(stack[i]) || isdigit(stack[i]))) return 1; // синтаксична помилка
    }
    if (output_tree == 0) return 1; // синтаксична помилка
    while (output_tree->getParentNode() != 0) output_tree = output_tree->getParentNode();
    return 0; // успішне завершення
}

int Parser::__sysParsing(const std::vector<TokenEntry> &token_table, std::string rules_stack, int &token_pos)
{
    Rules grammar_rule;
    if (token_pos == token_table.size())
    {
        if (__sysParsingCompletion(rules_stack)) return 1; // синтаксична помилка
        return 0; // успішне завершення
    }
    std::string token_name = token_table[token_pos].token_name;
    std::string token_real_name = token_table[token_pos].real_name;
    TokenType token_type = token_table[token_pos].token_type;
    while (!rules_stack.empty() && (isspace(rules_stack[0]) || iscntrl(rules_stack[0]))) rules_stack.erase(0, 1);
    if (!rules_stack.empty()) {
        std::cout << rules_stack << '\n';
        std::string tmp_rules_elements;
        if (islower(rules_stack[0])) // VT-символи - abc
        {
            uint rules_element;
            for (rules_element = 0; rules_element < rules_stack.size(); rules_element++)
                if (!islower(rules_stack[rules_element])) break;
            tmp_rules_elements = rules_stack.substr(0, rules_element);
            if (tmp_rules_elements != token_name) return 1; // синтаксична помилка
            rules_stack.erase(0, rules_element);
            while (output_tree->getRulesCounter() == 0) output_tree = output_tree->getParentNode();
            output_tree->insertOutputTree(new TreeOutput(token_name, token_real_name, token_type, 0));
            return __sysParsing(token_table, rules_stack, ++token_pos);
        }
        else if (!isalpha(rules_stack[0]) && !isdigit(rules_stack[0])) // VT-символи - {,(,[,]
        {
            uint rules_element;
            for (rules_element = 0; rules_element < rules_stack.size(); rules_element++) {
                char stack_symbol = rules_stack[rules_element];
                if (isalpha(stack_symbol) || isspace(stack_symbol) || isdigit(stack_symbol) || (rules_element==1)) break;
            }
            tmp_rules_elements = rules_stack.substr(0, rules_element);
            if (tmp_rules_elements != token_name) return 1; // синтаксична помилка
            rules_stack.erase(0, rules_element);
            while (output_tree->getRulesCounter() == 0) output_tree = output_tree->getParentNode();
            output_tree->insertOutputTree(new TreeOutput(token_real_name, token_type, 0));
            return __sysParsing(token_table, rules_stack, ++token_pos);
        }
        else if (isdigit(rules_stack[0])) // VT-символи - 123
        {
            uint rules_element;
            for (rules_element = 0; rules_element < rules_stack.size(); rules_element++)
                if (!isdigit(rules_stack[rules_element])) break;
            tmp_rules_elements = rules_stack.substr(0, rules_element);
            if (tmp_rules_elements != token_name) return 1; // синтаксична помилка
            rules_stack.erase(0, rules_element);
            while (output_tree->getRulesCounter() == 0) output_tree = output_tree->getParentNode();
            output_tree->insertOutputTree(new TreeOutput(token_real_name, token_type, 0));
            return __sysParsing(token_table, rules_stack, ++token_pos);
        }
        else // VN-символи
        {
            uint rules_element;
            for (rules_element = 0; rules_element < rules_stack.size(); rules_element++)
                if (!(isupper(rules_stack[rules_element]) || isdigit(rules_stack[rules_element]))) break;
            tmp_rules_elements = rules_stack.substr(0, rules_element); // вибираємо верхній символ з стека
            rules_stack.erase(0, rules_element);
        }
        grammar_rule = findRule(tmp_rules_elements, follow_rules);
        std::string nt_symbol = grammar_rule.getNTSymbol();
        TreeOutput *tmp_tree = new TreeOutput(tmp_rules_elements);
        if (output_tree != 0)
        {
            while (output_tree->getRulesCounter() == 0)
            {
                output_tree = output_tree->getParentNode();
                if (output_tree == 0) break;
            }
            if (output_tree != 0) output_tree->insertOutputTree(tmp_tree);
        }
        output_tree = tmp_tree;
        for (uint i = 0; i < grammar_rule.size(); i++) {
            std::string rules_elements;
            std::string specific_rule = grammar_rule.getRule(i);
            if (islower(specific_rule[0])) // VT-символи - var
                for (uint j = 0; j < specific_rule.size(); j++) {
                    if (!islower(specific_rule[j])) break;
                    rules_elements += specific_rule[j];
                }
            else if (!isalpha(specific_rule[0]) && !isdigit(specific_rule[0])) // VT-символи - (,[,{
            {
                for (uint j = 0; j < specific_rule.size(); j++) {
                    if (isalpha(specific_rule[j]) || isspace(specific_rule[j]) || isdigit(specific_rule[j])) break;
                    rules_elements += specific_rule[j];
                }
            }
            else if (isdigit(specific_rule[0])) // VT-символи - 123
                for (uint j = 0; j < specific_rule.size(); j++) {
                    if (!isdigit(specific_rule[j])) break;
                    rules_elements += specific_rule[j];
                }
            else // VN-символи
                for (uint j = 0; j < specific_rule.size(); j++) {
                    if (!(isupper(specific_rule[j]) || isdigit(specific_rule[j]))) break;
                    rules_elements += specific_rule[j];
                }
            if (!isupper(rules_elements[0])) // VT-символи
            {
                if (rules_elements == token_name)
                {
                    std::string lnt_symbol;
                    if (grammar_rule.isRepeatItself()) lnt_symbol = grammar_rule.getNTSymbol();
                    rules_stack.insert(0, specific_rule.substr(rules_elements.size(), specific_rule.size()) + " " + lnt_symbol + " ");
                    output_tree->setRulesCounter(grammar_rule.ruleSymbolCounter(i));

                    output_tree->insertOutputTree(new TreeOutput(token_name, token_real_name, token_type, 0));
                    return __sysParsing(token_table, rules_stack, ++token_pos);
                }
                else if (grammar_rule.size() == 1) return 1;
            }
            else // VN-символи
            {
                std::vector<std::string>::iterator it = std::find(first[rules_elements].begin(), first[rules_elements].end(), token_name);
                if (it != first[rules_elements].end())
                {
                    std::string lnt_symbol;
                    if (grammar_rule.isRepeatItself())
                    {
                        lnt_symbol = grammar_rule.getNTSymbol();
                        output_tree->getParentNode()->modifyRulesCounter(1);
                    }
                    rules_stack.insert(0, specific_rule + " " + lnt_symbol + " ");
                    output_tree->setRulesCounter(grammar_rule.ruleSymbolCounter(i));
                    return __sysParsing(token_table, rules_stack, token_pos);
                }
            }
        }
        std::vector<std::string>::iterator it = std::find(follow[nt_symbol].begin(), follow[nt_symbol].end(), token_name);
        if (it != follow[nt_symbol].end())
        {
            output_tree->insertOutputTree(new TreeOutput("^", 0));
            output_tree->setRulesCounter(0);
            return __sysParsing(token_table, rules_stack, token_pos);
        }
    }
    if (__sysParsingCompletion(rules_stack)) return 1; // синтаксична помилка
    return 0; // успішне завершення
}

int Parser::__fillIdentificatorTable(std::map<std::string, IdentificatorEntry> &identificator_table, TreeOutput *tree)
{
    int var_counter = 0;
    if (!isupper(tree->getNodeName()[0])) return 0; // VT-вузол не може мати дочірніх вузлів
    std::vector<TreeOutput*> &child_nodes = tree->getChildNodes();
    for (int i = 0; i < child_nodes.size(); i++) {
        if (child_nodes[i]->getNodeName() == "DATATYPE") // описання змінної
        {
            std::string varname = child_nodes[i + 1]->getNodeName(); // наступний вузол - ім'я змінної, яку об'являють
            if (!identificator_table[varname].isDeclaratedVar()) // синтаксична помилка - не може бути дві змінні з однаковим ім'ям
            {
                std::cout << "error: double revelation of variable\n";
                return -1;
            }
            std::string var_type = child_nodes[i]->getChildNodes()[0]->getNodeName(); // тип змінної
            IdentificatorType identificator_type;
            ushort var_size = 0;
            if (var_type == "symbol")
            {
                identificator_type = Symbol;
                var_size = 1;
            }
            else if (var_type == "bool")
            {
                identificator_type = Bool;
                var_size = 1;
            }
            else if (var_type == "string")
            {
                identificator_type = String;
                var_size = 4;
            }
            if (child_nodes[i + 2]->getChildNodes().size() > 4)
            {
                if (child_nodes[i + 2]->getChildNodes()[4]->getNodeName() == "MASDECLARATION")
                {
                    identificator_type = (IdentificatorType) (identificator_type | Array);
                    identificator_table[varname].setTypeVar(identificator_type);
                    const char *array_size = child_nodes[i + 2]->getChildNodes()[1]->getNodeName().c_str();
                    identificator_table[varname].setTypeSize(var_size);
                    identificator_table[varname].setArraySize(atoi(array_size));
                    identificator_table[varname].setNameVar(varname);
                }
            }
            else
            {
                identificator_table[varname].setTypeVar(identificator_type);
                identificator_table[varname].setTypeSize(var_size);
                identificator_table[varname].setNameVar(varname);
            }
            if (tree->getNodeName() == "FUNCPARAM") // параметри ф-ції
                identificator_table[varname].setIsFunctionParametr();
            ++var_counter;
        }
        else
        {
            int tmp_var_counter = __fillIdentificatorTable(identificator_table, tree->getChildNodes()[i]);
            if (tmp_var_counter == -1) return -1; // синтаксична помилка - не може бути дві змінні з однаковим ім'ям
            else if (tmp_var_counter != 0) var_counter += tmp_var_counter;
        }
    }
    return var_counter;
}
